/*
Written by Best Flash Solutions, Inc  
See:
	http://www.bestflashsolutions.com
Contact me: contact@bestflashsolutions.com
*/

/**
 * Red5 Streaming Process
 */
package com.bestflashsolutions.red5Streaming
{
    import com.bestflashsolutions.CommonUtils;
    import com.bestflashsolutions.app.AccessPermission;
    import com.bestflashsolutions.app.AppUserDataInfo;
    import com.bestflashsolutions.app.Message;
    import com.bestflashsolutions.phpRequest.PHPRequest;
    import com.bestflashsolutions.view.DJSingleChat;
    
    import flash.events.AsyncErrorEvent;
    import flash.events.Event;
    import flash.events.NetStatusEvent;
    import flash.events.StatusEvent;
    import flash.filters.DropShadowFilter;
    import flash.media.Video;
    import flash.net.NetConnection;
    import flash.net.NetStream;
    import flash.net.ObjectEncoding;
    import flash.net.SharedObject;
    import flash.utils.setTimeout;
    
    import mx.collections.ArrayCollection;
    import mx.collections.Sort;
    import mx.controls.Alert;
    import mx.controls.Button;
    import mx.controls.List;
    import mx.controls.VideoDisplay;
    import mx.core.Application;
    import mx.core.UIComponent;
    import mx.utils.UIDUtil;
    
    //Control streaming record
    public class LiveStreaming {

        private var nc:NetConnection;
        //For current user publishing
		private var nsPublisher: NetStream;
		
		public var parent: WebVideoConference; 
		private var movieSetting: MovieSetting; //record configuration
		public var currentState: AppStates;		//can be WAITING, PUBLISHING, REPLAYING
		private var objInformation: MyObject;
		//List of user Streaming object
		public var objPlayLiveList: PlayLiveList;
		private var guidUserName:String = UIDUtil.createUID();
		
		public function LiveStreaming(parentContainer: WebVideoConference, configuration: MovieSetting, objInfor: MyObject) {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +" LiveStreaming::LiveStreaming()");
			parent = parentContainer;
			movieSetting = configuration;
			currentState = AppStates.ST_IDLE;
			objInformation = objInfor;						
		} 

		//Start join into room
        public function join():void {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +" LiveStreaming::join()");
        	//Step 1: Get list joiners
        	currentState = AppStates.ST_LISTJOINERS;
        	var req: PHPRequest = new PHPRequest(objInformation.sPHPRequest, checkRsRoomJoiners);
			var params = req.params;
			
			params["command"] = "roomjoiners";
			params["username"] = objInformation.username;
			params["password"] = objInformation.password;
			params["roomId"] = objInformation.roomID;
			//Send to PHP
			req.request();

        }

		//Logout room
        private function realLogoutRoom():void {
        	currentState = AppStates.ST_IDLE;
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::realLogoutRoom()");
			if (objPlayLiveList) objPlayLiveList.stopAll();
			if (nc) nc.close();
			objPlayLiveList = null;
			
        }
        
        //Logout room
        public function logoutRoom():void {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + objInformation.username + " LiveStreaming::logoutRoom()");
			realLogoutRoom();
			objInformation.emptyStreamName();
        }

		//Event when all users video have been received.
		public function onCompleteGetAllStreaming(rs: Boolean) {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::onCompleteGetAllStreaming()");
			if (rs) {
				currentState = AppStates.ST_UPDATEROOMJOINER;
				//Update DB
				updateJoiner();
			}
			else {
				//Error; Stop
				currentState = AppStates.ST_IDLE;
				Message.ERR_RTMPSERVER_STREAMING_ERROR.alert();
			}			
		}

		//Get list of joiner in room
		public function checkRsRoomJoiners(event: Event) {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::checkRsRoomJoiners()");
			// check the message that was sent back from the .php file
			var message: String = event.target.data;
				
			try {
				var objRs:XML = new XML(event.target.data);
				trace("live stream joiners: "+objRs.toXMLString());
				objInformation.emptyStreamName();
				var i: int = 0;
					
				objInformation.lstJoinerFull = objRs;
				//Check total room
				for each (var objJoiner: XML in objRs.joiner) {
					var joiner: String = objJoiner.toString();
					if (joiner!=objInformation.username)
						i++;
				}
				//Check max joiner condition				
				if ((i+1) > objInformation.maxJoiner) {
					Message.ERR_ROOM_FULL.alert();
					currentState = AppStates.ST_IDLE;
					return;
				}
				
				//Store joiners name
				i = 1;
				for each (var objJoiner: XML in objRs.joiner) {
					var joiner: String = objJoiner.toString();
					if (joiner!=objInformation.username && joiner!=objInformation.creator) {
						//Check max joiner condition again
						if ((i+1) > objInformation.maxJoiner) {
							Message.ERR_ROOM_FULL.alert();
							currentState = AppStates.ST_IDLE;
							return;
						}
						objInformation.lstJoiner[i++] = joiner;
					}
				}
				
				//Find creator
				for each (var objJoiner: XML in objRs.joiner) {
					if (objJoiner.toString()==objInformation.creator) {
						break;
					} 
				}
				
				//Assign creator in correct position
				if (objInformation.isCreator || objInformation.username==objInformation.creator) {
					objInformation.lstJoiner[0] = objInformation.creator;
				}
				else {
					if (objInformation.iPosition == -1)
						objInformation.iPosition = i;
					//Check max joiner condition again
					if ((i+1) > objInformation.maxJoiner) {
						Message.ERR_ROOM_FULL.alert();
						currentState = AppStates.ST_IDLE;
						return;
					}
					//Save creator position
					objInformation.lstJoiner[i] = objInformation.lstJoiner[objInformation.iPosition];
					objInformation.lstJoiner[objInformation.iPosition ] = objInformation.username;	
				}
				
				currentState = AppStates.ST_JOINROOM;
				//Start get all video
				connectAll();
			}
			catch (e:Error) {
				new Message(Message.ERROR, "Server error: " + e.toString()).alert();
			}
		}

		//Connect to Red5 and receive all video
        public function connectAll():void
        {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::connectAll()");
            // new netconnection
            nc = new NetConnection();
            // set encoding to old amf
            nc.objectEncoding = ObjectEncoding.AMF0;
            // netstatus event listening
            nc.addEventListener(NetStatusEvent.NET_STATUS , netStatus );	
			nc.client = new EventManager(this);//this;
            // connect to red5
            movieSetting.streamName = objInformation.roomGUID;
			trace("rtmp://" +  movieSetting.RTMPServer + "/" + movieSetting.getAppName() + "/" +  movieSetting.getStreamName());
			nc.connect("rtmp://" +  movieSetting.RTMPServer + "/" + movieSetting.getAppName() + "/" +  movieSetting.getStreamName(), "room", objInformation.roomID, objInformation.username);
			
			CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + "Connect...");
        }
        
		//Send current user video to red5
		
		
		public function streamingNow(): void {
			
			publish();
		}
				       	
		//publish stream to server red5
		public function publish():void {			
			CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::publish()");
			nsPublisher = new NetStream(nc);
        	nsPublisher.client = new EventManager(this);
			nsPublisher.addEventListener(NetStatusEvent.NET_STATUS , netStatus );	
			//Check if video or audio is exist
			if (movieSetting.objMicrophone) nsPublisher.attachAudio(movieSetting.objMicrophone);
			if (movieSetting.objCamera) nsPublisher.attachCamera(movieSetting.objCamera);
			
			if (movieSetting.objCamera != null)
				movieSetting.objCamera.addEventListener(StatusEvent.STATUS, statusHandler);
			//start  with record 
			
			updateVideoHistory(0);
			updateJoiner();
			
			
			nsPublisher.publish(guidUserName, "record");
        }
		
		public function changeMicCamHandler(camMic:String):void{
			if(camMic == "audiodevice"){
				if (movieSetting.objMicrophone) nsPublisher.attachAudio(movieSetting.objMicrophone);
			}else{
				if (movieSetting.objCamera) nsPublisher.attachCamera(movieSetting.objCamera);
			}
		}
		
		private function statusHandler(event:StatusEvent):void {
			trace(event.code);
			var videoMute:int = 0;
			if(event.code == "Camera.Unmuted")
				videoMute = 1;
			
			updateVideoHistory(videoMute);

		}
		
		private function updateVideoHistory(videoMute:int):void{
			var req: PHPRequest = new PHPRequest(objInformation.sPHPRequest, checkRsVideoInsert);
			var params = req.params;
			
			params["command"] = "videohistory";
			params["vidguid"] = guidUserName;
			params["viduser"] = objInformation.username;
			params["vidroom"] = objInformation.roomGUID;
			params["videMute"] = videoMute;
			//Request PHP to save
			req.request();
		}
		
		private function checkRsVideoInsert(event: Event) {
			trace(event.target.data);
		}

		//Show stream of all others
		private function showAllStream() {
			if(objPlayLiveList){
				objPlayLiveList.stopAll();
				objPlayLiveList = null;
			}
			
			CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::showAllStream()");
			trace("showAllStream->"+objInformation.username)
			objPlayLiveList = new PlayLiveList(nc, objInformation);
			objPlayLiveList.crrPos = 0;
			
			objPlayLiveList.objInformation = objInformation;
			objPlayLiveList.onComplete = onCompleteGetAllStreaming;
			//Start receiving
			objPlayLiveList.playAll();
			
			setTimeout(function hello():void{Application.application.cvsRoom.updateLayOut()}, 1000);
		}
		
		//receive status of connection	    
		public function netStatus ( event:NetStatusEvent ):void
	    {
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::netStatus: " + event.info.code);
	        trace(event.info.code);
			
			if ( event.info.code == "NetConnection.Connect.Rejected" ) {
	            CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + "netStatus: " + event.info.application );
	            currentState == AppStates.ST_IDLE;
				Message.ERR_RTMPSERVER_REJECT.alert();
	        }
	        else if ( event.info.code == "NetConnection.Connect.Failed" ) {
	            CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + "netStatus: " + event.info.application );
	            currentState == AppStates.ST_IDLE;
	            Message.ERR_RTMPSERVER_CANNOT_CONNECT.alert();
	        }
	        else if ( event.info.code == "NetConnection.Connect.Success" ) {
	            CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + "netStatus: " + event.info.application );
	            currentState=AppStates.ST_STREAMING;
	            //Connect OK so start publish now
				streamingNow();
	        }
	        
	        if ( event.info.code == "NetStream.Publish.Start" ) {
				currentState = AppStates.ST_GETSTREAMING;
				//receive published
				showAllStream();
	        }
	    }

		//update current room state on DB
        public function updateJoiner():void {
			CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " + objInformation.username + " LiveStreaming::updateJoiner()");
        	currentState = AppStates.ST_UPDATEROOMJOINER;
        	var req: PHPRequest = new PHPRequest(objInformation.sPHPRequest, checkRsRoomManagerUpdated);
			var params = req.params;
			
			//Alert.show("agi ko sa update join");
			
			params["command"] = "updateroommanager";
			params["username"] = objInformation.username;
			params["password"] = objInformation.password;
			params["roomId"] = objInformation.roomID;
			//Store size of current video
			params["cam_width"] = movieSetting.width;
			params["cam_height"] = movieSetting.height;
			//Request PHP to save
			req.request();
        }
		
	    
	    //Check if updateJoiner is success?
	    public function checkRsRoomManagerUpdated(event: Event) {
			CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + " LiveStreaming::checkRsRoomManagerUpdated()");
			// check the message that was sent back from the .php file
			var message: String = event.target.data;
			try {
				var objRs:XML = new XML(event.target.data);
				//If OK
				if (objRs[0]==1) {
						currentState = AppStates.ST_INWORKING;
						//Rearrange video windows
						if (objInformation.autoArrange)
							objPlayLiveList.autoarrange();
						
				}
				else {
					//Error , so force logout
					logoutRoom();
					parent.cmdConnect.label = "Connect";
					currentState = AppStates.ST_IDLE;
					Message.ERR_WEBSERVER_SAVE.alert();
				}					
			}
			catch (e:Error) {
				new Message(Message.ERROR, "Server error: " + e.message).alert();
			}
			Application.application.cvsRoom.updateLayOut();
		}

		
	    public function asyncErrorHandler(event:AsyncErrorEvent):void { 
	        CommonUtils.log((new Date()).toTimeString() + "-" + (new Date()).getMilliseconds() + " = " +objInformation.username + "AsynError: " + event.text); 
        } 
		
		//force video be center of container
		private function centerUp(videoDisplay: UIComponent, container:UIComponent):void
		{
			if ((videoDisplay.width <= container.width) && (videoDisplay.height <= container.height)) { 
				var center:Number = Math.abs((videoDisplay.width/2)-(container.width/2));
				var middle:Number = Math.abs((videoDisplay.height/2)-(container.height/2))
				videoDisplay.x = center //(mainStage.width/2);
				videoDisplay.y = middle //(mainStage.height/2);
			}
		}
        
		//control volume
		public function volumeControl(i: Number):void {
/*			if (currentState==AppStates.REPLAYING && nsVOD!=null) {
				var st:SoundTransform = nsVOD.soundTransform;
				st.volume = i;
				nsVOD.soundTransform = st;
			}
			*/
		}
		
		//check room every period and process
		public function update(objRs: Object) {
			var i: int;
			//1. Remove session if not exist anymore
			trace("live streaing update->")
			for (i=0; i<objPlayLiveList.lstLiveStream.length; i++) {
				var objPlayLive: PlayLive = objPlayLiveList.lstLiveStream[i] as PlayLive;
				if (objPlayLive.available) {
					var found: Boolean = false;
					
					//Check if session still exist
					for each (var joiner: String in objRs.joiner) {
						if (objPlayLive.streamName==joiner) {
							found = true;
							break;
						}
					}
					if (!found) {
						//Remove
						objPlayLive.stop();
						objInformation.lstJoiner[i]="";
						objPlayLive.streamName = ""; 
						objPlayLive.streamGuid = "";
						objPlayLive.permission = "";
						objPlayLive.available = false;
						objPlayLive.joinDate = 0;
						objPlayLive.isNew = false;
						Application.application.cvsRoom.updateLayOut();
						if(objPlayLive.getChildByName("singleChat")){
							var chatDownButton:Button = objPlayLive.getChildByName("chatDownButton") as Button;
							var singleChat:DJSingleChat = objPlayLive.getChildByName("singleChat") as DJSingleChat;
							singleChat.destroyTHis();
							objPlayLive.removeChild(chatDownButton);
							objPlayLive.removeChild(singleChat);
						}
						//objPlayLive.cam_height = 320;
						//objPlayLive.cam_width = 320;
						
					}
				}
			}
			
			//2. Add new session
			for each (var joiner: String in objRs.joiner) {
				var found: Boolean = false;
				var objPlayLive: PlayLive;
				for (i=0; i<objInformation.maxJoiner; i++) {
					objPlayLive = objPlayLiveList.lstLiveStream[i] as PlayLive;
					//Check if session not exist
					trace(joiner,"==",objPlayLive.streamName);
					if (objPlayLive.available && joiner==objPlayLive.streamName) {
						var objJoiners = objInformation.findRoomJoiner(joiner);
						if (objJoiners){ 
							objPlayLive.updateStreamGuid(objJoiners.@vidguid);
						}				
						found = true;
						break;
					}
				}
				
				//New session --> show video			
				if (!found && joiner!=objInformation.username) {
					
					/*for(var id:String in objRs) {
						var value:Object = objRs[id];
						
						Alert.show(id + " = " + value);
					}*/
					
					for (i=0; i<objInformation.maxJoiner; i++) {
						objPlayLive = objPlayLiveList.lstLiveStream[i] as PlayLive;
						//var objJoiners:XML = objInformation.findRoomJoiner(joiner);
						trace(i)
						if (!objPlayLive.available || objPlayLive.streamName=="") {
							//Add
							objInformation.lstJoiner[i]=joiner;
							objPlayLive.streamName = joiner; 
							objPlayLive.available = true;
							objPlayLive.joinDate = new Date().getTime();
							objPlayLive.isNew = true;
							var objJoiner = objInformation.findRoomJoiner(objInformation.lstJoiner[i]);
							
							trace("joiners: ",joiner);
							//Get their video size
							if (objJoiner) {
								//objPlayLive.cam_height = objJoiner.@cam_height;
								//objPlayLive.cam_width = objJoiner.@cam_width
								objPlayLive.streamGuid = objJoiner.@vidguid;
								objPlayLive.permission = objJoiner.@permission

							} 
							//Start get video
							
							objPlayLive.play();
							objPlayLive.forceMaximize();
							objPlayLive.x = i * 5 * MyObject.PADDING;
							
							break;							
						}
					}
					
					var showRoom:Boolean = true;
					var roomName:String = "";
					if((objInformation.permission == AccessPermission.INSTRUCTOR && objJoiner.@permission == AccessPermission.INSTRUCTOR) ||
					   (objJoiner.@permission == AccessPermission.ADMINISTRATOR && objInformation.permission == AccessPermission.ADMINISTRATOR)){
						if(int(objJoiner.@userid) < AppUserDataInfo.getInstance().userId)
							roomName = objInformation.username+"-"+joiner;
						else
							roomName = joiner+"-"+objInformation.username;
					}else if((objJoiner.@permission == AccessPermission.ADMINISTRATOR && objInformation.permission == AccessPermission.INSTRUCTOR) ||
					   (objJoiner.@permission == AccessPermission.ADMINISTRATOR && objInformation.permission == AccessPermission.STUDENT) ||
					   (objJoiner.@permission == AccessPermission.INSTRUCTOR && objInformation.permission == AccessPermission.STUDENT)){
						roomName = objInformation.username+"-"+joiner;
					}else
						roomName = joiner+"-"+objInformation.username;
					
					//Alert.show("room name 1: "+roomName);
					
					// create one on one chat
					
					if(showRoom && (objInformation.permission == AccessPermission.INSTRUCTOR || 
						objInformation.permission == AccessPermission.ADMINISTRATOR ||
						objJoiner.@permission == AccessPermission.ADMINISTRATOR || 
						objJoiner.@permission == AccessPermission.INSTRUCTOR)){		
						
						objPlayLive.addChatControls(roomName,joiner);
					}
					//Check if max joiner condition
					if (i==objInformation.maxJoiner) {
						Message.ERR_ROOM_FULL_REFRESH.alert();
						break;
					}
				}
			}
			
			Application.application.cvsRoom.updateLayOut();
			//If autoarrange select so rearrange video windows
			if (objInformation.autoArrange)
				objPlayLiveList.autoarrange();
		}
   	}
}
